﻿// <copyright file="CandidateDetails.cs" company="Microsoft Corporation">
// Copyright (c) 2009 Microsoft Corporation All Rights Reserved
// </copyright>
// <author>Michael S. Scherotter</author>
// <email>mischero@microsoft.com</email>
// <date>2009-06-11</date>
// <summary>CandidateDetails class definition</summary>

namespace NYTimes
{
    using System;
    using System.Globalization;
    using System.Text;
    using NYTimes.Core;
    using NYTimes.Data;

    /// <summary>
    /// New York Times Campaign Finance Campaign Details API
    /// </summary>
    public class CandidateDetails : CampaignFinance
    {
        #region Fields
        /// <summary>
        /// the campaign year
        /// </summary>
        private int year = 2008;

        /// <summary>
        /// the candidate name
        /// </summary>
        private string candidateName;

        /// <summary>
        /// the candidate ID
        /// </summary>
        private string candidateId;

        /// <summary>
        /// the candidate details that were returned
        /// </summary>
        private Candidate candidate;
        #endregion

        /// <summary>
        /// Gets or sets the year. (currently 2008 is the only available year and is the default
        /// value)
        /// </summary>
        public int Year
        {
            get
            {
                return this.year;
            }

            set
            {
                if (this.year != value)
                {
                    this.year = value;

                    FetchData();
                }
            }
        }

        /// <summary>
        /// Gets or sets the candidate name
        /// </summary>
        /// <remarks>
        /// {last-name}[,first-name]
        /// first-name is optional. If you include a first name, there should be no space after the 
        /// comma.
        /// If you do not include a first-name, data will be returned for all candidates with the 
        /// last-name you specify.
        /// </remarks>
        public string CandidateName
        {
            get
            {
                return this.candidateName;
            }

            set
            {
                if (this.candidateName != value)
                {
                    this.candidateName = value;

                    this.candidateId = null;

                    FetchData();
                }
            }
        }

        /// <summary>
        /// Gets or sets the candidate Id
        /// </summary>
        /// <remarks>
        /// C + 9 digits.  The FEC assigns a <a 
        /// href="http://www.fec.gov/pages/brochures/candregis.shtml#Registration_PCC" 
        /// target="_blank">unique ID</a> to each campaign committee.
        /// </remarks>
        public string CandidateId
        {
            get
            {
                return this.candidateId;
            }

            set
            {
                if (this.candidateId != value)
                {
                    this.candidateId = value;

                    this.candidateName = null;

                    FetchData();
                }
            }
        }

        /// <summary>
        /// Gets the candidate details
        /// </summary>
        public Candidate Results
        {
            get
            {
                return this.candidate;
            }

            private set
            {
                if (this.candidate != value)
                {
                    this.candidate = value;

                    TriggerPropertyChanged("Candidate");
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether there is a CandidateId or a CandidateName
        /// </summary>
        protected override bool CanFetchData
        {
            get
            {
                if (string.IsNullOrEmpty(this.CandidateId) && string.IsNullOrEmpty(this.CandidateName))
                {
                    return false;
                }

                return true;
            }
        }

        /// <summary>
        /// Clear the candidate property
        /// </summary>
        protected override void ClearResults()
        {
            this.Results = null;
        }

        /// <summary>
        /// Generate a web address for the service
        /// </summary>
        /// <param name="baseAddress">the base address</param>
        /// <returns>the URL for the service</returns>
        protected override Uri GenerateAddress(string baseAddress)
        {
            var builder = new StringBuilder(baseAddress);

            if (string.IsNullOrEmpty(this.CandidateName))
            {
                builder.AppendFormat(
                    CultureInfo.InvariantCulture,
                    "{0}/finances/candidates/{1}.xml?api-key={2}",
                    this.Year,
                    this.CandidateId,
                    this.ApiKey);
            }
            else 
            {
                builder.AppendFormat(
                    CultureInfo.InvariantCulture,
                    "{0}/finances/candidates/{1}.xml?api-key={2}",
                    this.Year,
                    this.CandidateName,
                    this.ApiKey);
            }

            return new Uri(builder.ToString(), UriKind.Absolute);
        }

        /// <summary>
        /// Set the Candidate object with the result 
        /// </summary>
        /// <param name="results">the results element</param>
        /// <returns>true to signify that there are no more results</returns>
        protected override bool ProcessResults(System.Xml.Linq.XElement results)
        {
            if (results == null)
            {
                throw new ArgumentNullException("results");
            }

            var candidateResult = results.Element("candidate");

            this.Results = new Candidate()
            {
                CashOnHand = GetCurrency(candidateResult, "cash_on_hand"),
                CommitteeId = candidateResult.Element("committee_id").Value,
                DateCoverageFrom = DateTime.Parse(candidateResult.Element("date_coverage_from").Value, CultureInfo.InvariantCulture),
                DateCoverageTo = DateTime.Parse(candidateResult.Element("date_coverage_to").Value, CultureInfo.InvariantCulture),
                FederalFunds = GetCurrency(candidateResult, "federal_funds"),
                Name = candidateResult.Element("candidate_name").Value,
                NetCandidateContributions = GetCurrency(candidateResult, "net_candidate_contributions"),
                NetGeneralContributions = GetCurrency(candidateResult, "net_general_contributions"),
                NetIndividualContributions = GetCurrency(candidateResult, "net_individual_contributions"),
                NetPACContributions = GetCurrency(candidateResult, "net_pac_contributions"),
                NetPartyContributions = GetCurrency(candidateResult, "net_party_contributions"),
                NetPrimaryContributions = GetCurrency(candidateResult, "net_primary_contributions"),
                Party = candidateResult.Element("party").Value,
                TotalContributions2300 = GetCurrency(candidateResult, "total_contributions_2300"),
                TotalContributionsLessThan200 = GetCurrency(candidateResult, "total_contributions_less_than_200"),
                TotalDisbursements = double.Parse(candidateResult.Element("total_disbursements").Value, CultureInfo.InvariantCulture),
                TotalReceipts = double.Parse(candidateResult.Element("total_receipts").Value, CultureInfo.InvariantCulture),
                TotalRefunds = GetCurrency(candidateResult, "total_refunds")
            };

            return true;
        }

        /// <summary>
        /// Get an XML element as currency
        /// </summary>
        /// <param name="element">the parent element</param>
        /// <param name="name">the element name</param>
        /// <returns>a double value for the currency amount</returns>
        private static double GetCurrency(System.Xml.Linq.XElement element, string name)
        {
            return double.Parse(element.Element(name).Value, CultureInfo.InvariantCulture);
        }
    }
}
